#!/usr/bin/env python2  
# encoding: utf-8  

""" 
@version: v1.0 
@author: whyc
@license: Apache Licence  
@contact: 8720whyc@gmail.com
@site:  
@software: PyCharm 
@file: 2-5seq_plus_multi.py 
@time: 17-8-3 下午1:08
@description :
    test t序列使用 + 和 * 的应用
"""


def func25():
    """
    + 和 * 都遵循这个规律， 不修改原有的操作对象， 而是构建一个全新的
    序列。
    如果在 a * n 这个语句中， 序列 a 里的元素是对其他可变
    对象的引用的话， 你就需要格外注意了， 因为这个式子的结果可能
    会出乎意料。 比如， 你想用 my_list = [[]] * 3 来初始化一个
    由列表组成的列表， 但是你得到的列表里包含的 3 个元素其实是 3
    个引用， 而且这 3 个引用指向的都是同一个列表。 这可能不是你想
    要的效果
    :return:
    """
    li = [1, 2, 3]
    print("5*'abc': ",5*'abc')
    print("test * 符号的引用 li * 5: li =[1,2,3] ", li * 5)
    ## 实例 2-12
    #3X3 的列表推导 ，其中应用了 * 来表达
    board =[['_']*3 for i in range(3)] #➊
    print("board: ",board)
    board[2][1]="wwwwwd"  # ➋
    print("赋值给 board[2][1]:%s"%board)

    """
        ➊ 建立一个包含 3 个列表的列表， 被包含的 3 个列表各自有 3 个元
        素。 打印出这个嵌套列表。
        ➋ 把第 1 行第 2 列的元素标记为 X， 再打印出这个列表。
        示例 2-13 展示了另一个方法， 这个方法看上去是个诱人的捷径， 但实
        际上它是错的。
    """
    ##实例 2-13
    #---------------------------------------
    # 不使用列表推导式 直接使用 * 乘积来计算
    weird_board=[['_']*3]*3  #➊
    print("wired_board: ",weird_board)
    # 赋值一个元素值
    weird_board[1][1]='00o' #➋
    print("赋值后的序列：",weird_board)

    """
        ➊ 外面的列表其实包含 3 个指向同一个列表的引用。 当我们不做修改
        的时候， 看起来都还好。
        ➋ 一旦我们试图标记第 1 行第 2 列的元素， 就立马暴露了列表内的 3
        个引用指向同一个对象的事实。
    """
    ########################################
    #---------------------------------------
    #实例 2-12,和2-13 的相同 执行方式：
    #实例 2-12.1 ,与 2-12 相同 结果
    board1 = []
    for i in range(3):
        row = ['_'] * 3 #➊ #相当与每次循环都重新创建了一个数组对象
        board1.append(row) #
    print("----------------------------")
    print(board1)
    board1[1][1] = "new"
    print(board1) # ➋
    """
        ➊ 每次迭代中都新建了一个列表， 作为新的一行（ row） 追加到游戏板
        （ board） 。
        ➋ 正如我们所期待的， 只有第 2 行的元素被修改。
        如果你觉得这一节里所说的问题及其对应的解决方法都有点
        云里雾里， 没关系。 第 8 章里我们会详细说明引用和可变对象背后
        的原理和陷阱。
        我们一直在说 + 和 *， 但是别忘了我们还有 += 和 *=。 随着目标序列的
        可变性的变化， 这个两个运算符的结果也大相径庭。 

    """

    #实例2-13.1 与实例
    #相反， 示例 2-13 中的方法等同于这样做：
    row=['_']*3
    board2 = []
    for i in range(3):
        board2.append(row) #➊  #在这里式每次引用的都式同一个对象，
    print(board2)
    board2[1][1] = "new two"
    print(board2) #
    """
        ➊ 追加同一个行对象（ row） 3 次到游戏板（ board） 。
    """

"""
2.6 序列的增量赋值

"""
def func26():
    """
        增量赋值运算符 += 和 *= 的表现取决于它们的第一个操作对象。 简单起
        见， 我们把讨论集中在增量加法（ +=） 上， 但是这些概念对 *= 和其他
        增量运算符来说都是一样的。
        += 背后的特殊方法是 __iadd__ （ 用于“就地加法”） 。 但是如果一个类
        没有实现这个方法的话， Python 会退一步调用 __add__ 。
        a += b
        --------------------------------------------------
        如果 a 实现了 __iadd__ 方法， 就会调用这个方法。 同时对可变序列
        （ 例如 list、 bytearray 和 array.array） 来说， a 会就地改动， 就
        像调用了 a.extend(b) 一样。 但是如果 a 没有实现 __iadd__ 的话， a
        += b 这个表达式的效果就变得跟 a = a + b 一样了： 首先计算 a +
        b， 得到一个新的对象， 然后赋值给 a。 也就是说， 在这个表达式中，
        变量名会不会被关联到新的对象， 完全取决于这个类型有没有实现
        __iadd__ 这个方法。
        总体来讲， 可变序列一般都实现了 __iadd__ 方法， 因此 += 是就地加
        法。 而不可变序列根本就不支持这个操作， 对这个方法的实现也就无从
        谈起。
    :return: NOTHING
    """
    li = [1,2,3]
    print("id(li): ",id(li)) #❶
    li *= 2
    print("li*=2 : ",li) #
    print("id(li) 通过乘法之后的ID:",id(li)) #❷
    #元组不可变
    t = (1,2,3)
    print("id(t): ", id(t))# ❸
    t *= 2
    print("id(t): t 元组通过增量乘法之后的id值： ", id(t))#❹

    """
        ❶ 刚开始时列表的 ID。
        ❷ 运用增量乘法后， 列表的 ID 没变， 新元素追加到列表上。
        ❸ 元组最开始的 ID。
        ❹ 运用增量乘法后， 新的元组被创建。
        对不可变序列进行重复拼接操作的话， 效率会很低， 因为每次都有一个
        新对象， 而解释器需要把原来对象中的元素先复制到新的对象里， 然后
        再追加新的元素 
        str 是一个例外， 因为对字符串做 += 实在是太普遍了， 所以 CPython 对它做了优化。 为 str
        初始化内存的时候， 程序会为它留出额外的可扩展空间， 因此进行增量操作的时候， 并不会涉
        及复制原有字符串到新位置这类操作
    """


class Main():
    def __init__(self):
        pass
    #func25()
    func26()


""" 
THIS IS THE PRINT RESULTS FOR THIS TEST FILE:
#--------------------------------------------------
func25() 的输出结果
5*'abc':  abcabcabcabcabc
test * 符号的引用 li * 5: li =[1,2,3]  [1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3, 1, 2, 3]
board:  [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
赋值给 board[2][1]:[['_', '_', '_'], ['_', '_', '_'], ['_', 'wwwwwd', '_']]
wired_board:  [['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
赋值后的序列： [['_', '00o', '_'], ['_', '00o', '_'], ['_', '00o', '_']]
----------------------------
[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
[['_', '_', '_'], ['_', 'new', '_'], ['_', '_', '_']]
[['_', '_', '_'], ['_', '_', '_'], ['_', '_', '_']]
[['_', 'new two', '_'], ['_', 'new two', '_'], ['_', 'new two', '_']]

#----------------------------------------------------
func26() 的输出结果
id(li):  139857721897416
li*=2 :  [1, 2, 3, 1, 2, 3]
id(li) 通过乘法之后的ID: 139857721897416
id(t):  139857721905512
id(t): t 元组通过增量乘法之后的id值：  139857747237672

"""
if __name__ == "__main__":
    Main()